package pacman.gui;

import jbrowser.pacman.demo.util.Pacman;
import jbrowser.pacman.demo.util.ConsoleDependencyGraphDumper;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import jbrowser.gui.Page;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.artifact.Artifact;
import org.sonatype.aether.collection.CollectRequest;
import org.sonatype.aether.collection.CollectResult;
import org.sonatype.aether.graph.Dependency;
import org.sonatype.aether.graph.DependencyNode;
import org.sonatype.aether.installation.InstallRequest;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.util.artifact.DefaultArtifact;
import org.sonatype.aether.util.artifact.SubArtifact;

public class PackageManager extends JPanel implements Page, ActionListener {

    private static final FileFilter fileFilter = new FileFilter() {
        @Override
        public boolean accept(File file) {
            if (file.isDirectory()) {
                return file.listFiles(fileFilter).length > 0;
            } else {
                return file.getName().endsWith(".jar");
            }
        }
    };

    @Override
    public void actionPerformed(ActionEvent e) {
        if ("ManuallyInstallArtifact".equals(e.getActionCommand())) {
            if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
                try {
                    File jarFile = fileChooser.getSelectedFile();
                    System.out.println("---------------------------------------------------------");
                    System.out.println("--------- install " + jarFile.getPath() + " -------------");
                    System.out.println("---------------------------------------------------------");
                    JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile));
                    JarEntry pomEntry = null;
                    JarEntry pomPropertiesEntry = null;
                    JarEntry jarEntry = jarInputStream.getNextJarEntry();
                    while (jarEntry != null) {
                        if (jarEntry.getName().startsWith("META-INF/maven/")) {
                            if (jarEntry.getName().endsWith("/pom.xml")) {
                                pomEntry = jarEntry;
                            } else if (jarEntry.getName().endsWith("/pom.properties")) {
                                pomPropertiesEntry = jarEntry;
                            }
                        }
                        if (pomEntry != null && pomPropertiesEntry != null) {
                            break;
                        } else {
                            jarEntry = jarInputStream.getNextJarEntry();
                        }
                    }
                    if (pomEntry == null || pomPropertiesEntry == null) {
                        System.out.println("--------- ERROR: pom.properties or pom.xml not found! -------------");
                        return;
                    }

                    ClassLoader classLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()});
                    InputStream pomPropertiesInputStream = classLoader.getResourceAsStream(pomPropertiesEntry.getName());

                    Properties pomProperties = new Properties();
                    pomProperties.load(pomPropertiesInputStream);
                    String version = pomProperties.getProperty("version");
                    String groupId = pomProperties.getProperty("groupId");
                    String artifactId = pomProperties.getProperty("artifactId");
                    if (groupId == null || artifactId == null || version == null) {
                        throw new RuntimeException("Not valid artifact coords!");
                    }
                    String coords = groupId + ":" + artifactId + ":" + version;
                    System.out.println("--------- COORDS: " + coords + " -------------");

                    System.out.println("--------- Copy pom.xml from jar to temp file -------------");
                    File pomFile = File.createTempFile("pom", "xml");
                    FileOutputStream output = new FileOutputStream(pomFile);
                    InputStream input = classLoader.getResource(pomEntry.getName()).openStream();
                    byte[] buffer = new byte[4096];
                    int bytesRead = input.read(buffer);
                    while (bytesRead != -1) {
                        output.write(buffer, 0, bytesRead);
                        bytesRead = input.read(buffer);
                    }
                    output.close();
                    input.close();
                    System.out.println("--------- " + pomFile.length() + " bytes copied -------------");

                    RepositorySystem system = Pacman.newRepositorySystem();
                    RepositorySystemSession session = Pacman.newRepositorySystemSession(system);
                    Artifact jarArtifact = new DefaultArtifact(coords);
                    jarArtifact = jarArtifact.setFile(jarFile);
                    Artifact pomArtifact = new SubArtifact(jarArtifact, "", "pom");
                    pomArtifact = pomArtifact.setFile(pomFile);
                    InstallRequest installRequest = new InstallRequest();
                    installRequest.addArtifact(jarArtifact).addArtifact(pomArtifact);
                    system.install(session, installRequest);

                } catch (Exception ex) {
                    ex.printStackTrace();
                    Logger.getLogger(PackageManager.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    public PackageManager() throws Exception {
        initComponents();

        btnManuallyInstallArtifact.addActionListener(this);

        final File repo = new File(Pacman.getLocalRepository());
        final DefaultMutableTreeNode root = new DefaultMutableTreeNode(repo.getName());
        final DefaultTreeModel model = new DefaultTreeModel(root);
        treeRepo.setModel(model);

        SwingWorker<DefaultTreeModel, DefaultMutableTreeNode> worker = new SwingWorker<DefaultTreeModel, DefaultMutableTreeNode>() {
            @Override
            public DefaultTreeModel doInBackground() {
                File[] files = repo.listFiles(fileFilter);
                progressBar.setMaximum(files.length);
                for (int i = 0; i < files.length; i++) {
                    publish(getTreeModel(files[i]));
                }
                return null;
            }

            @Override
            protected void process(List<DefaultMutableTreeNode> chunks) {
                for (DefaultMutableTreeNode node : chunks) {
                    model.insertNodeInto(node, root, root.getChildCount());
                    progressBar.setValue(root.getChildCount());
                }
                treeRepo.expandPath(new TreePath(root.getPath()));
            }

            @Override
            protected void done() {
                try {
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        };
        worker.execute();

        // Add a listener
        treeRepo.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                try {
                    System.out.println("--------- " + e.getPath().toString() + " -------------");
                    Object path[] = e.getPath().getPath();
                    if (path.length < 5 || !path[path.length - 1].toString().endsWith(".jar")) {
                        return;
                    }
                    String version = path[path.length - 2].toString();
                    String artifactId = path[path.length - 3].toString();
                    String groupId = path[1].toString();
                    for (int i = 2; i < path.length - 3; i++) {
                        groupId = groupId + "." + path[i].toString();
                    }
                    String coords = groupId + ":" + artifactId + ":" + version;
                    System.out.println("--------- coord: " + coords + " -------------");

                    RepositorySystem system = Pacman.newRepositorySystem();
                    RepositorySystemSession session = Pacman.newRepositorySystemSession(system);
                    RemoteRepository repo = Pacman.newCentralRepository();
                    Artifact artifact = new DefaultArtifact(coords);
                    CollectRequest collectRequest = new CollectRequest();
                    collectRequest.setRoot(new Dependency(artifact, ""));
                    collectRequest.addRepository(repo);
                    CollectResult collectResult = system.collectDependencies(session, collectRequest);
                    collectResult.getRoot().accept(new ConsoleDependencyGraphDumper());

                    treeDependencies.setModel(new DefaultTreeModel(getTreeModel(collectResult.getRoot())));

                } catch (Exception ex) {
                    ex.printStackTrace();
                    Logger.getLogger(PackageManager.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }

    private DefaultMutableTreeNode getTreeModel(DependencyNode root) {
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(root);
        for (DependencyNode node : root.getChildren()) {
            rootNode.add(getTreeModel(node));
        }
        return rootNode;
    }

    private DefaultMutableTreeNode getTreeModel(File root) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode(root.getName());
        if (root.isDirectory()) {
            File[] files = root.listFiles(fileFilter);
            for (int i = 0; i < files.length; i++) {
                node.add(getTreeModel(files[i]));
            }
        }
        return node;
    }

    @Override
    public String getTitle() {
        return "Package Manager";
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        fileChooser = new javax.swing.JFileChooser();
        lblMessage = new javax.swing.JLabel();
        jSplitPane1 = new javax.swing.JSplitPane();
        jPanel1 = new javax.swing.JPanel();
        btnManuallyInstallArtifact = new javax.swing.JButton();
        scrRepo = new javax.swing.JScrollPane();
        treeRepo = new javax.swing.JTree();
        progressBar = new javax.swing.JProgressBar();
        tbpKeyStore = new javax.swing.JTabbedPane();
        scrTree = new javax.swing.JScrollPane();
        treeDependencies = new javax.swing.JTree();
        scrList = new javax.swing.JScrollPane();
        tblDependencies = new javax.swing.JTable();

        setPreferredSize(new java.awt.Dimension(512, 256));
        setLayout(new java.awt.BorderLayout(1, 1));

        lblMessage.setFont(new java.awt.Font("Tahoma", 1, 14)); // NOI18N
        lblMessage.setText("Certificate Manager");
        lblMessage.setVerticalAlignment(javax.swing.SwingConstants.TOP);
        add(lblMessage, java.awt.BorderLayout.CENTER);

        jPanel1.setLayout(new java.awt.GridBagLayout());

        btnManuallyInstallArtifact.setText("Manually install artifact");
        btnManuallyInstallArtifact.setActionCommand("ManuallyInstallArtifact");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
        jPanel1.add(btnManuallyInstallArtifact, gridBagConstraints);

        javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("root");
        treeRepo.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
        treeRepo.setRootVisible(false);
        scrRepo.setViewportView(treeRepo);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 0.1;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
        jPanel1.add(scrRepo, gridBagConstraints);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(0, 1, 0, 1);
        jPanel1.add(progressBar, gridBagConstraints);

        jSplitPane1.setLeftComponent(jPanel1);

        treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("Select item to show dependency tree");
        treeDependencies.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
        scrTree.setViewportView(treeDependencies);

        tbpKeyStore.addTab("Dependency tree", scrTree);

        tblDependencies.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "Group Id", "Artifact Id", "Version"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class, java.lang.Object.class
            };
            boolean[] canEdit = new boolean [] {
                false, false, false
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        scrList.setViewportView(tblDependencies);

        tbpKeyStore.addTab("Dependency list", scrList);

        jSplitPane1.setRightComponent(tbpKeyStore);

        add(jSplitPane1, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnManuallyInstallArtifact;
    private javax.swing.JFileChooser fileChooser;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JLabel lblMessage;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JScrollPane scrList;
    private javax.swing.JScrollPane scrRepo;
    private javax.swing.JScrollPane scrTree;
    private javax.swing.JTable tblDependencies;
    private javax.swing.JTabbedPane tbpKeyStore;
    private javax.swing.JTree treeDependencies;
    private javax.swing.JTree treeRepo;
    // End of variables declaration//GEN-END:variables

    @Override
    public JComponent getComponent() {
        return this;
    }

    @Override
    public Icon getIcon() {
        return null;
    }

    @Override
    public CacheMode getCacheMode() {
        return null;
    }
}
